package com.ilearn.actor;


import com.ilearn.util.NamedThreadFactory;

import java.util.Collection;
import java.util.Map;
import java.util.Stack;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;


public final class ActorSystem {

	private final String name;
	
	private final Map<String, ActorContext> contexts = new ConcurrentHashMap<String,ActorContext>();
	
	private final ScheduledExecutorService pool;
	
	private final Dispatcher dispatcher;
	
	private volatile int status = RUN;
	
	protected final static int STOP=0;
	protected final static int RUN=1;
	protected final static int SHUTDOWN=2;
	
	
	public ActorSystem(String name) {
		this(name, Runtime.getRuntime().availableProcessors() < 5 ? 5: Runtime.getRuntime().availableProcessors());
	}
	
	public ActorSystem(String name, int count) {
		this.name = name;
		this.pool = Executors.newScheduledThreadPool(count, new NamedThreadFactory(name,false));
		
		this.dispatcher = new Dispatcher(this);
	}
	
	public void monitor(ActorContext ctx) {
		this.dispatcher.monitor(ctx);
	}
	
	protected Collection<ActorContext> contexts() {
		return this.contexts.values();
	}
	
	public String name() {
		return this.name;
	}
	
	public int status() {
		return this.status;
	}
	
	public ScheduledExecutorService executorService() {
		return this.pool;
	}
	
	public ActorContext actorRegist(String path, Actor actor) {
		
		if(this.status() != RUN)
			throw new RuntimeException("Actor System closed.");
		
		ActorContext ctx = new ActorContext(this, path, actor);
		actor.setContext(ctx);
		try {
			actor.preStart();
		} catch (Exception e) {
			
			e.printStackTrace();
		}
		contexts.put(path, ctx);
		return ctx;
		
	}
	
	public ActorContext select(String path) {
		return (ActorContext) this.contexts.get(path);
	}
	
	public void shutdown() {
		
		Thread t = new Thread(new Runnable() {

			@Override
			public void run() {
				for(ActorContext ac: contexts.values()) {
					try {
						ac.actor().postStop();
					} catch (Exception e) {
						
						e.printStackTrace();
					}
				}
				
			}
			
		});
			

		t.start();
		
		this.status = STOP;
		this.dispatcher.shutdown();
		this.pool.shutdown();
	}
	
	public void shutdownGracefully() {
		this.status = SHUTDOWN;
		
		Stack<ActorContext> stack = new Stack<ActorContext>();
		stack.addAll(this.contexts.values());
		
		int count = 0;
		
		while(!stack.isEmpty() && count < 100) {
			count ++;
			ActorContext ctx = stack.pop();
			if(!ctx.empty()) {
				if(ctx.isSuspend())
					ctx.suspend(false);
				stack.push(ctx);
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {}
			}
		}
		
		this.shutdown();
	}
	

}
